package com.itboy.search.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.itboy.common.core.domain.entity.es.SkuEsModel;
import com.itboy.common.utils.JsonUtils;
import com.itboy.search.config.ElasticSearchConfig;
import com.itboy.search.constant.EsConstant;
import com.itboy.search.service.MallSearchService;
import com.itboy.search.vo.SearchParam;
import com.itboy.search.vo.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MallSearchServiceImpl implements MallSearchService {

	@Autowired
	private RestHighLevelClient restHighLevelClient;

	@Override
	public SearchResult search(SearchParam param) {
		SearchResult searchResult = null;
		//1. 创建检索请求
		SearchRequest searchRequest = buildSearchRequest(param);
		try {
			SearchResponse searchResponse = restHighLevelClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
			searchResult = buildSearchResult(param, searchResponse);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return searchResult;
	}

	/**
	 * 构建返回结果
	 * @param param
	 * @param searchResponse
	 * @return
	 */
	private SearchResult buildSearchResult(SearchParam param, SearchResponse searchResponse) {
		SearchResult searchResult = new SearchResult();
		//1.检索出来的数据
		SearchHits searchHits = searchResponse.getHits();
		SearchHit[] hitsHits = searchHits.getHits();
		//2.组装
		List<SkuEsModel> skuEsModels = null;
		if (hitsHits.length > 0 && hitsHits != null) {
			skuEsModels = Arrays.asList(hitsHits).stream().map(sbuHit -> {
				String sourceAsString = sbuHit.getSourceAsString();
				SkuEsModel skuEsModel = JsonUtils.parseObject(sourceAsString, SkuEsModel.class);
				if (!StringUtils.isEmpty(param.getKeyword())) {
					HighlightField skuTitle = sbuHit.getHighlightFields().get("skuTitle");
					String skuTitleHighLight = skuTitle.getFragments()[0].string();
					skuEsModel.setSkuTitle(skuTitleHighLight);
				}
				return skuEsModel;
			}).collect(Collectors.toList());
			List<SearchResult.AttrVo> attrVoList = new ArrayList<>();
			skuEsModels.forEach(item->{
				for (SkuEsModel.Attrs attr : item.getAttrs()) {
					SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
					//if(!attrVoList.contains(attr)){
						attrVo.setAttrId(attr.getAttrId());
						attrVo.setAttrName(attr.getAttrName());
						attrVo.setAttrValue(Arrays.asList(attr.getAttrValue().split(",")));
						attrVoList.add(attrVo);
					//}
				}
			});

			searchResult.setAttrs(attrVoList);
		}
		//3.返回所查询到的所有商品
		searchResult.setProducts(skuEsModels);
		//4.当前所有商品所涉及到的所有属性信息
		ParsedNested attrAgg = searchResponse.getAggregations().get("attr_agg");
		if(attrAgg.getDocCount()>0){
			ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attr_id_agg");
				List<SearchResult.AttrVo> attrVos = attrIdAgg.getBuckets().stream().map(item -> {
					SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
					//1.获取属性的id
					long attrId = item.getKeyAsNumber().longValue();

					//2.获取属性名
					String attrName = ((ParsedStringTerms) item.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();

					//3.获取属性的所有值
					List<String> attrValues = ((ParsedStringTerms) item.getAggregations().get("attr_value_agg")).getBuckets().stream().map(bucket -> {
						return bucket.getKeyAsString();
					}).collect(Collectors.toList());

					attrVo.setAttrId(attrId);
					attrVo.setAttrName(attrName);
					attrVo.setAttrValue(attrValues);
					return attrVo;
				}).collect(Collectors.toList());
				searchResult.setAttrs(attrVos);
		}


		//5.当前所有商品所涉及到的所有品牌信息
		ParsedLongTerms brandAgg = searchResponse.getAggregations().get("brand_agg");
		if(ObjectUtil.isNotNull(brandAgg)){
			List<SearchResult.BrandVo> brandVos = brandAgg.getBuckets().stream().map(item -> {
				SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
				//1.获取id
				long brandId = item.getKeyAsNumber().longValue();
				//2.获取品牌名
				String brandName = ((ParsedStringTerms) item.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();

				//3.获取品牌图片
				String brandImg = ((ParsedStringTerms) item.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();

				brandVo.setBrandId(brandId);
				brandVo.setBrandName(brandName);
				brandVo.setBrandImg(brandImg);
				return brandVo;
			}).collect(Collectors.toList());
			searchResult.setBrands(brandVos);
		}


		//6.当前所有商品所涉及到的所有分类信息
		ParsedLongTerms catalogAgg = searchResponse.getAggregations().get("catalog_agg");
		if(ObjectUtil.isNotNull(catalogAgg)){
			List<SearchResult.CatalogVo> catalogVos = catalogAgg.getBuckets().stream().map(item -> {
				SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
				//1.获取分类id
				String catalogId = ((Terms.Bucket) item).getKeyAsString();
				catalogVo.setCatalogId(Long.parseLong(catalogId));
				//2.获取分类名称
				ParsedStringTerms catalogNameAgg = item.getAggregations().get("catalog_name_agg");
				String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
				catalogVo.setCatalogName(catalogName);
				return catalogVo;
			}).collect(Collectors.toList());
			searchResult.setCatalogs(catalogVos);
		}

		//7.分页
		searchResult.setPageNum(param.getPageNum());
		//总条数
		long total = searchHits.getTotalHits().value;
		searchResult.setTotal(total);
		//总页码
		boolean flag=total%EsConstant.PRODUCT_PAGE_SIZE == 0;
		int totalPage=flag?(int)total/EsConstant.PRODUCT_PAGE_SIZE:((int)total/EsConstant.PRODUCT_PAGE_SIZE)+1;
		searchResult.setTotalPages(totalPage);
		ArrayList<Integer> page = new ArrayList<>();
		for (int i=1;i<=totalPage;i++){
			page.add(i);
		}

		searchResult.setPageNavs(page);
		return searchResult;
	}

	/**
	 * 准备检索请求
	 * 模糊匹配，过滤（按照属性，分类，品牌，价格区间，库存），排序，分页，高亮，聚合分析
	 *
	 * @param param 检索条件
	 * @return
	 */
	private SearchRequest buildSearchRequest(SearchParam param) {
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		// 构建bool-query
		BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
		//1. bool-must按关键字全文检索
		if (StringUtils.isNotBlank(param.getKeyword())) {
			boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
		}
		//2.按分类id查询
		if (null != param.getCatalog3Id()) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
		}
		//3.按品牌id查询
		if (null != null) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("brandId", param.getBrandId()));
		}
		//4.按属性查询
		if (CollectionUtil.isNotEmpty(param.getAttrs())) {
			param.getAttrs().forEach(attr -> {
				//例：attr=1_5寸:8寸&2_16G:8G    1为属性id 5寸:8寸 属性值
				BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
				String[] str_attr = attr.split("_");
				//属性id
				String attrId = str_attr[0];
				//属性值
				String[] attrValues = str_attr[1].split(":");
				boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
				boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));

				NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
				boolQueryBuilder.filter(nestedQueryBuilder);
			});
		}
		//5.查询库存不为0的
		if (null != param.getHasStock()) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
		}
		//6.按价格区间查询
		if (StringUtils.isNotBlank(param.getSkuPrice())) {
			//价格区间查询 1_500或_500或500_
			RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
			String[] price = param.getSkuPrice().split("_");
			if (price.length == 2) {
				rangeQueryBuilder.gte(price[0]).lte(price[1]);
			} else if (price.length == 1) {
				if (param.getSkuPrice().startsWith("_")) {
					rangeQueryBuilder.lte(price[1]);
				}
				if (param.getSkuPrice().endsWith("_")) {
					rangeQueryBuilder.gte(price[0]);
				}
			}
			boolQueryBuilder.filter(rangeQueryBuilder);
		}
		//封装所有查询条件
		searchSourceBuilder.query(boolQueryBuilder);

		/**
		 * 排序，分页，高亮
		 */
		//1.排序
		if (StringUtils.isNotBlank(param.getSort())) {
			String sort = param.getSort();
			String[] sortFileds = sort.split("_");
			SortOrder sortOrder = "asc".equalsIgnoreCase(sortFileds[1]) ? SortOrder.ASC : SortOrder.DESC;
			searchSourceBuilder.sort(sortFileds[0], sortOrder);
		}
		//2.分页
		searchSourceBuilder.from((param.getPageNum() - 1) * EsConstant.PRODUCT_PAGE_SIZE);
		searchSourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
		//3.高亮
		if (StringUtils.isNotBlank(param.getKeyword())) {
			HighlightBuilder highlightBuilder = new HighlightBuilder();
			highlightBuilder.field("skuTitle");
			highlightBuilder.preTags("<b style='color:red'>");
			highlightBuilder.postTags("</b>");
			searchSourceBuilder.highlighter(highlightBuilder);
		}

		/**
		 * 聚合分析
		 */

		//1.按品牌进行聚合
		TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg");
		brandAgg.field("brandId").size(50);
		//1.1 品牌的子聚合-按品牌名称聚合
		brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
		//1.2 品牌的子聚合-按品牌图片聚合
		brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
		searchSourceBuilder.aggregation(brandAgg);

		//2.按分类信息进行聚合
		TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalog_agg");
		catalogAgg.field("catalogId").size(20);
		catalogAgg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
		searchSourceBuilder.aggregation(catalogAgg);

		//3.按属性进行聚合
		NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attr_agg", "attrs");
		//3.1按属性子聚合—按id信息聚合
		TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
		attrAgg.subAggregation(attrIdAgg);
		//3.2在每个属性id下，按属性名称进行聚合
		attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
		//3.3在每个属性id下，按属性值进行聚合
		attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
		searchSourceBuilder.aggregation(attrAgg);
		log.debug("构建DSL语句 {}", searchSourceBuilder.toString());
		SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
		return searchRequest;
	}
}
